using System;
using System.Collections.Generic;
using System.Linq;
using NHibernate;
using NHibernate.Criterion;
using NHibernate.Linq;

namespace Sample.Service
{
    /// <summary>
    /// Generic query and persistence provider for business objects.
    /// Service classes can inherit from this to do more specific
    /// operations
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <remarks>Taken verbatim from http://davybrion.com/blog/2008/06/data-access-with-nhibernate/
    /// then trimmed down just a touch. You should wrap any operations in a transaction
    /// but that implementation is left up to you because you may have transactions
    /// that span multiple operations.</remarks>
    public abstract class Repository<T>
    {
        protected readonly ISession _session;

        protected Repository(ISession session)
        {
            _session = session;
        }

        /// <summary>
        /// Get a single object by its unique ID
        /// </summary>
        /// <param name="id">The ID - NHibernate knows how to cast
        /// to the correct value</param>
        /// <returns>A single match</returns>
        public virtual T Get(object id)
        {
            return _session.Get<T>(id);
        }

        protected virtual void SaveOrUpdate(T entity)
        {
            _session.SaveOrUpdate(entity);
        }

        protected virtual void Delete(T entity)
        {
            _session.Delete(entity);
        }

        protected virtual IEnumerable<T> Find(DetachedCriteria criteria)
        {
            return criteria.GetExecutableCriteria(_session).List<T>();
        }

        protected virtual T FindOne(DetachedCriteria criteria)
        {
            return criteria.GetExecutableCriteria(_session).UniqueResult<T>();
        }

        protected virtual long Count(DetachedCriteria criteria)
        {
            return Convert.ToInt64(criteria.GetExecutableCriteria(_session)
                .SetProjection(Projections.RowCountInt64()).UniqueResult());
        }

        public virtual long Count()
        {
            var query = _session.Linq<T>();
            return (query).Count();
        }
    }
}